#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>
#include "DataProc.h"

enum class DataCmd : uint16_t
{
    Invalid = 0,
    FileUpload,
    AddUploader,
    FileBlock,
    FileData,
    TransferFinish,

    // 内部命令
    GetFileInfoReq = 100,
    GetFileInfoRes
};

#pragma pack(push, 1)
struct IntMsgHead
{
    DataCmd dc;
    ClntNode *pNode;
};
#pragma pack(pop)

CDataProc::CDataProc(ClntNode *pNode) : m_pClntNode(pNode)
{
    m_fd = -1;
    m_iFileLen = 0;
}

CDataProc::~CDataProc()
{
}

// #include <string>
// bool CDataProc::DataHandle(const uint8_t *const pData, uint16_t nLen)
// {
//     std::string strTest((char *)pData, nLen);
//     printf("Length=%d, Content:%s\n", nLen, strTest.c_str());

//     void *pBlock = malloc(nLen);

//     if (nullptr == pBlock)
//         return false;

//     memcpy(pBlock, pData, nLen);
//     int iSent = m_pClntNode->pClnt->SendData(pBlock, nLen);

//     free(pBlock);

//     return iSent >= nLen;
// }

bool CDataProc::DataHandle(const uint8_t *pData, uint16_t nLen)
{
    bool bRet = false;
    const uint8_t *ptr = pData;
    DataCmd cmd = *(DataCmd *)ptr;

    //去除数据包类型
    ptr += sizeof(DataCmd);
    nLen -= sizeof(DataCmd);

    switch (cmd)
    {
    case DataCmd::FileUpload:
        bRet = FileUploadRequest(ptr, nLen);
        break;

    case DataCmd::AddUploader:
        bRet = AddUploaderRequest(ptr, nLen);
        break;

    case DataCmd::FileBlock:
        bRet = FileBlockRequest(ptr, nLen);
        break;

    case DataCmd::FileData:
        bRet = FileDataRequest(ptr, nLen);
        break;

    case DataCmd::TransferFinish:
        bRet = TransferFinish(ptr, nLen);
        break;

    case DataCmd::GetFileInfoReq:
        bRet = GetFileInfoRequest(ptr, nLen);
        break;

    case DataCmd::GetFileInfoRes:
        bRet = GetFileInfoResponse(ptr, nLen);
        break;

    default:
        printf("Error: invalid command.\n");
        break;
    }

    return bRet;
}

bool CDataProc::FileUploadRequest(const uint8_t *pData, uint16_t nLen)
{
    char *pName = nullptr;
    const uint8_t *ptr = pData;

    // 获得多少个客户端同时上传
    uint16_t nClntNum = *(uint16_t *)ptr;
    ptr += sizeof(uint16_t);
    nLen -= sizeof(uint16_t);

    // 获得文件长度
    int64_t iFileSize = *(int64_t *)ptr;
    ptr += sizeof(int64_t);
    nLen -= sizeof(int64_t);

    // 获得文件名
    pName = (char *)ptr;
    printf("worker=%d,name=%s,length=%ld\n", nClntNum, pName, iFileSize);

    // 创建文件并让文件长度为iFileSize
    int fd = open(pName, O_RDWR | O_CREAT | O_TRUNC, 0666);

    if (-1 == fd)
        return false;

    lseek64(fd, iFileSize - 1, SEEK_END);
    write(fd, "\0", 1);
    close(fd);

    m_iFileLen = iFileSize;
    m_strFile = pName;

    // 发送反馈数据包
    // TODO：内存可不回收使用
    uint8_t *pSendData = (uint8_t *)malloc(256);

    if (nullptr == pSendData)
        return false;

    ptr = pSendData;

    // 请求包类型
    *(DataCmd *)ptr = DataCmd::FileUpload;
    uint16_t nPkgSize = sizeof(DataCmd);
    ptr += sizeof(DataCmd);

    // 处理结果
    *(bool *)ptr = true;
    nPkgSize += sizeof(bool);
    ptr += sizeof(bool);

    int iCore = get_nprocs();
    // 允许的客户端个数
    *(uint16_t *)ptr = (uint16_t)(iCore >= nClntNum ? nClntNum : iCore);
    nPkgSize += sizeof(uint16_t);
    ptr += sizeof(uint16_t);

    // 上传数据客户端需发送该数据标识是需要上传的哪个文件业务
    *(uint64_t *)ptr = (uint64_t)m_pClntNode;
    nPkgSize += sizeof(uint64_t);
    ptr += sizeof(uint64_t);

    int iSent = m_pClntNode->pClnt->SendData(pSendData, nPkgSize);
    free(pSendData);

    return (iSent >= nPkgSize);
}

bool CDataProc::AddUploaderRequest(const uint8_t *pData, uint16_t nLen)
{
    // uint8_t *ptr = nullptr;
    ClntNode *pNode = *(ClntNode **)pData;
    CClientMgr *pClntMgr = CIodpModule::GetClientMgr();

    if (!pClntMgr->IsValidNode(pNode))
        return false;

    IntMsgHead imh;
    imh.dc = DataCmd::GetFileInfoReq;
    imh.pNode = m_pClntNode;
    m_pClntNode->pClnt->IpcSendDataInt(pNode->pSvr, pNode->pClnt, &imh, sizeof(imh));

    return true;
}

bool CDataProc::GetFileInfoRequest(const uint8_t *pData, uint16_t nLen)
{
    ClntNode *pNode = *(ClntNode **)pData;

    IntMsgHead imh;
    imh.dc = DataCmd::GetFileInfoRes;
    imh.pNode = m_pClntNode;

    uint8_t *pSendData = (uint8_t *)malloc(256);

    if (nullptr == pSendData)
        return false;

    memcpy(pSendData, &imh, sizeof(imh));

    uint8_t *ptr = pSendData + sizeof(imh);
    uint16_t nPkgSize = sizeof(imh);

    *(int64_t *)ptr = m_iFileLen;
    nPkgSize += sizeof(int64_t);
    ptr += sizeof(int64_t);

    uint16_t nStrLen = m_strFile.length();
    memcpy(ptr, m_strFile.c_str(), nStrLen);
    nPkgSize += nStrLen;
    ptr += nStrLen;

    *ptr = 0;
    nPkgSize += 1;

    m_pClntNode->pClnt->IpcSendDataInt(pNode->pSvr, pNode->pClnt, pSendData, nPkgSize);
    free(pSendData);

    return true;
}

bool CDataProc::GetFileInfoResponse(const uint8_t *pData, uint16_t nLen)
{
    const uint8_t *ptr = pData;
    // ClntNode *pNode = *(ClntNode **)pData;
    ptr += sizeof(ClntNode *);

    m_iFileLen = *(int64_t *)ptr;
    ptr += sizeof(int64_t);

    m_strFile = (char *)ptr;

    m_fd = open((char *)ptr, O_RDWR, 0666);

    if (-1 == m_fd)
        return false;

    // 发送反馈数据包
    // TODO：内存可不回收使用
    uint8_t *pSendData = (uint8_t *)malloc(256);

    if (nullptr == pSendData)
        return false;

    ptr = pSendData;

    // 请求包类型
    *(DataCmd *)ptr = DataCmd::AddUploader;
    uint16_t nPkgSize = sizeof(DataCmd);
    ptr += sizeof(DataCmd);

    // 处理结果
    *(bool *)ptr = true;
    nPkgSize += sizeof(bool);
    ptr += sizeof(bool);

    int iSent = m_pClntNode->pClnt->SendData(pSendData, nPkgSize);
    free(pSendData);

    return (iSent >= nPkgSize);
}

bool CDataProc::FileBlockRequest(const uint8_t *pData, uint16_t nLen)
{
    const uint8_t *ptr = pData;
    int64_t iOffset = *(int64_t *)ptr;
    ptr += sizeof(int64_t);

    uint32_t nLength = *(uint32_t *)ptr;
    m_nBlockLen = nLength;
    m_nCurLen = 0;

    m_pBlockData = (uint8_t *)mmap(nullptr, nLength, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, iOffset);

    if (MAP_FAILED == m_pBlockData)
    {
        printf("Error: mmap line=%d\n", __LINE__);
        return false;
    }

    // 发送反馈数据包
    // TODO：内存可不回收使用
    uint8_t *pSendData = (uint8_t *)malloc(128);

    if (nullptr == pSendData)
        return false;

    ptr = pSendData;

    // 请求包类型
    *(DataCmd *)ptr = DataCmd::FileBlock;
    uint16_t nPkgSize = sizeof(DataCmd);
    ptr += sizeof(DataCmd);

    // 处理结果
    *(bool *)ptr = true;
    nPkgSize += sizeof(bool);
    ptr += sizeof(bool);

    int iSent = m_pClntNode->pClnt->SendData(pSendData, nPkgSize);
    free(pSendData);

    return (iSent >= nPkgSize);
}

bool CDataProc::FileDataRequest(const uint8_t *pData, uint16_t nLen)
{
    if (nullptr == m_pBlockData)
        return false;

    memcpy(m_pBlockData + m_nCurLen, pData, nLen);
    m_nCurLen += nLen;

    if (m_nCurLen >= m_nBlockLen)
    {
        munmap(m_pBlockData, m_nBlockLen);
        m_pBlockData = nullptr;
        m_nBlockLen = 0;
        m_nCurLen = 0;
    }

    return true;
}

bool CDataProc::TransferFinish(const uint8_t *pData, uint16_t nLen)
{
    bool bFinish = *(bool *)pData;

    if (bFinish)
    {
        if (nullptr != m_pBlockData)
        {
            printf("Error: TransferFinish m_pBlockData\n");
        }

        if (-1 != m_fd)
        {
            close(m_fd);
            m_fd = -1;
        }
    }

    return true;
}